package com.lxm.framework.web.jsonresult.filter;

import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.PropertyFilter;
import com.fasterxml.jackson.databind.ser.PropertyWriter;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;

import java.util.*;

/**
 * @author twenty2
 */
@JsonFilter("jsonResultFilter")
public class JsonResultProvider extends SimpleFilterProvider {

    private final Map<Class<?>, Set<String>> includeMap = new HashMap<>();
    private final Map<Class<?>, Set<String>> excludeMap = new HashMap<>();

    public Map<Class<?>, Set<String>> getIncludeMap(){
        return this.includeMap;
    }

    public Map<Class<?>, Set<String>> getExcludeMap(){
        return this.excludeMap;
    }

    public void include(Class<?> type, String[] fields) {
        this.addToMap(includeMap, type, fields);
    }

    public void exclude(Class<?> type, String[] fields) {
        this.addToMap(excludeMap, type, fields);
    }

    private void addToMap(Map<Class<?>, Set<String>> map, Class<?> type, String[] fields) {
        var fieldSet = map.getOrDefault(type, new HashSet<>());
        fieldSet.addAll(Arrays.asList(fields));
        map.put(type, fieldSet);
    }

    @Override
    public PropertyFilter findPropertyFilter(Object filterId, Object valueToFilter) {
        return new SimpleBeanPropertyFilter() {
            @Override
            public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer) throws Exception {
                if (this.include(pojo.getClass(), writer.getName())) {
                    writer.serializeAsField(pojo, jgen, provider);
                } else if (!jgen.canOmitFields()) {
                    writer.serializeAsOmittedField(pojo, jgen, provider);
                }
            }

            public boolean include(Class<?> type, String name) {
                var includeFields = includeMap.get(type);
                var excludeFields = excludeMap.get(type);

                if (includeFields != null) {
                    return includeFields.contains(name);
                } else if (excludeFields != null) {
                    return !excludeFields.contains(name);
                } else{
                    return true;
                }
            }
        };
    }
}
